﻿using System;
using System.Data;
using System.Collections.Generic;

namespace Optum.SN.Infra.Core.Cryptography
{
    /// <summary>
    /// Data transfer type for communicating cryptography data with database.
    /// </summary>
    public class CryptoUpdaterList : List<CryptoUpdater>
    {
        /// <summary>
        /// Converts the underlying list to <see cref="DataTable"/>.
        /// </summary>
        /// <returns></returns>
        public DataTable ToDataTable()
        {
            var dt = CryptoUpdater.GetTemplateDataTable();
            ForEach(model => dt.Rows.Add(model.Values));
            return dt;
        }
        /// <summary>
        /// Creates list with either plain text or cipher text values.
        /// </summary>
        /// <param name="dataList">Data source with either plain text or cipher text values.</param>
        /// <param name="isPlainText">Specifies whether the supplied values are plain text or cipher text.</param>
        public CryptoUpdaterList(List<string> dataList, bool isPlainText)
        {
            if (isPlainText)
                dataList.ForEach(data => Add(new CryptoUpdater {PlainText = data}));
            else
                dataList.ForEach(data => Add(new CryptoUpdater {CipherText = data}));
        }
        /// <summary>
        /// Creates empty list.
        /// </summary>
        public CryptoUpdaterList() { }
        /// <summary>
        /// Hydrates underlying list from JSON.
        /// </summary>
        /// <param name="json">Data source with two columns, PlainText and CipherText.</param>
        public CryptoUpdaterList(string json)
        {
            var startIndex = json.IndexOf("{", StringComparison.Ordinal) + 1;
            while (startIndex < json.Length - 2)
            {
                var endIndex = json.IndexOf("}", startIndex, StringComparison.Ordinal);
                Add(ToCryptoUpdater(json.Substring(startIndex, endIndex - startIndex)));
                startIndex = endIndex + 3;
            }
        }
        /// <summary>
        /// Hydrates underlying list from <see cref="IDataReader"/>.
        /// </summary>
        /// <param name="reader">Data source with two columns, PlainText and CipherText.</param>
        public CryptoUpdaterList(IDataReader reader)
        {
            while (reader.Read())
                Add(new CryptoUpdater(reader.GetString(0), reader.GetString(1)));
        }
        /// <summary>
        /// Gets the plain text corresponding to a specific cipher text value from underlying <see cref="CryptoUpdater"/> list.
        /// </summary>
        /// <param name="cipherText">The cipher text to find in the underlying list.</param>
        /// <returns>The plain text corresponding to the specific cipher text value.</returns>
        public string GetPlainText(string cipherText)
        {
            for (var i = 0; i < Count; i++)
                if (this[i].CipherText == cipherText)
                    return this[i].PlainText;
            return null;
        }
        /// <summary>
        /// Gets the cipher text corresponding to a specific plain text value from underlying <see cref="CryptoUpdater"/> list.
        /// </summary>
        /// <param name="plainText">The plain text to find in the underlying list.</param>
        /// <returns>The cipher text corresponding to the specific plain text value.</returns>
        public string GetCipherText(string plainText)
        {
            for (var i = 0; i < Count; i++)
                if (this[i].PlainText == plainText)
                    return this[i].CipherText;
            return null;
        }
        /// <summary>
        /// Determines whether the underlying <see cref="CryptoUpdater"/> list contains a specific plain text value.
        /// </summary>
        /// <param name="plainText">The plain text to find in the underlying list.</param>
        /// <returns>True when match found.</returns>
        public bool ContainsPlainText(string plainText)
        {
            for (var i = 0; i < Count; i++)
                if (this[i].PlainText == plainText)
                    return true;
            return false;
        }
        /// <summary>
        /// Determines whether the underlying <see cref="CryptoUpdater"/> list contains a specific cipher text value.
        /// </summary>
        /// <param name="cipherText">The cipher text to find in the underlying list.</param>
        /// <returns>True when match found.</returns>
        public bool ContainsCipherText(string cipherText)
        {
            for (var i = 0; i < Count; i++)
                if (this[i].CipherText == cipherText)
                    return true;
            return false;
        }
        /// <summary>
        /// Gets the list of plain text from the underlying <see cref="CryptoUpdater"/> list.
        /// </summary>
        /// <returns>List of plain text.</returns>
        public List<string> GetPlainTextList()
        {
            var plainTextList = new List<string>();
            for (var i = 0; i < Count; i++)
                plainTextList.Add(this[i].PlainText);
            return plainTextList;
        }
        /// <summary>
        /// Gets the list of cipher text from the underlying <see cref="CryptoUpdater"/> list.
        /// </summary>
        /// <returns>List of cipher text.</returns>
        public List<string> GetCipherTextList()
        {
            var cipherTextList = new List<string>();
            for (var i = 0; i < Count; i++)
                cipherTextList.Add(this[i].CipherText);
            return cipherTextList;
        }
        /// <summary>
        /// Converts a JSON record to <see cref="CryptoUpdater"/> type.
        /// </summary>
        /// <param name="jsonRecord">The JSON record to convert.</param>
        /// <returns>Hydrated <see cref="CryptoUpdater"/> instance.</returns>
        private CryptoUpdater ToCryptoUpdater(string jsonRecord)
        {
            var fields = jsonRecord.Split(",".ToCharArray());
            return fields[0].Contains("PlainText")
                ? new CryptoUpdater(GetValue(fields[0]), GetValue(fields[1]))
                : new CryptoUpdater(GetValue(fields[1]), GetValue(fields[0]));
        }
        /// <summary>
        /// Gets value from a JSON record.
        /// </summary>
        /// <param name="jsonKeyValue">JSON record, contains a key value pair.</param>
        /// <returns>JSON value.</returns>
        private string GetValue(string jsonKeyValue)
        {
            var startIndex = jsonKeyValue.IndexOf(":", StringComparison.Ordinal) + 2;
            var endIndex = jsonKeyValue.LastIndexOf(@"""", StringComparison.Ordinal);
            return jsonKeyValue.Substring(startIndex, endIndex - startIndex);
        }
    }
    /// <summary>
    /// Cryptography data transfer type, used for transferring data to be en/decrypted to database.
    /// </summary>
    public sealed class CryptoUpdater
    {
        /// <summary>
        /// Gets/sets plain text value.
        /// </summary>
        public string PlainText { get; set; }
        /// <summary>
        /// Gets/sets cipher text value.
        /// </summary>
        public string CipherText { get; set; }
        /// <summary>
        /// Gets object array of plain text and cipher text values.
        /// </summary>
        public object[] Values
        {
            get { return new object[] {PlainText, CipherText}; }
        }
        /// <summary>
        /// Initializes plain text and cipher text values.
        /// </summary>
        /// <param name="plainText">Sets the plain text value.</param>
        /// <param name="cipherText">Sets the cipher text value.</param>
        public CryptoUpdater(string plainText, string cipherText)
        {
            PlainText = plainText;
            CipherText = cipherText;
        }
        /// <summary>
        /// Creates an uninitialized instance.
        /// </summary>
        public CryptoUpdater()
        {
        }
        /// <summary>
        /// Gets <see cref="DataTable"/> with PlainText and CipherText <see cref="DataColumn"/>s.
        /// </summary>
        internal static DataTable GetTemplateDataTable()
        {
            var dt = new DataTable();
            Action<string> addColumn = name =>
            {
                dt.Columns.Add(new DataColumn(name, typeof(string))
                {
                    MaxLength = 50,
                    ReadOnly = false
                });
            };
            addColumn("PlainText");
            addColumn("CipherText");
            return dt;
        }
    }

    class Temp
    {
    }
}